/*
* rc文件版本资源定义语法
resource-define statement:

resourceid VERSININFO
fixedfileInfo
BEBIN
	BLOCK "StringFileInfo"
	BEGIN
		BLOCK "LANG-CODEPAGE"
		BEGIN
			VALUE "CompanyName","LinkVic,Ltd"
		END

	END

	BEGIN
		BLOCK "VarFileInfo"
		BEGIN
			VALUE "Translation" 0x0409,1252
		END
	END
END

*/

#include <filesystem>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>

#include "vrtsVersion/vrtsVersion.hpp"

namespace vrts
{
	namespace version
	{
		static std::string GetVarFileInfo(_In_ LPCVOID pBlock)
		{
			UINT uLen = 0;

			std::ostringstream langcp;

			struct LANGANDCODEPAGE
			{
				WORD wLanguage;
				WORD wCodePage;
			} *lang = NULL;
			if (VerQueryValue(pBlock, "\\VarFileInfo\\Translation", (void **)&lang, &uLen))
			{
				langcp << std::setfill('0') << std::setw(4) << std::hex << lang->wLanguage;
				langcp << std::setfill('0') << std::setw(4) << std::hex << lang->wCodePage;
			}
			return langcp.str();
		}

		static std::string GetStringFileInfo(_In_ LPCVOID pBlock, _In_ std::string lpSubBlock)
		{
			std::string ret = "";
			UINT uLen = 0;
			LPVOID lpBuffer = NULL;
			if (VerQueryValue(pBlock, lpSubBlock.c_str(), (LPVOID *)&lpBuffer, &uLen))
			{
				ret = (char *)lpBuffer;
			}
			return ret;
		}

		FileVersionInfo GetFileVersionInfoLW(const std::string &file)
		{
			if (!std::filesystem::exists(file))
			{
				std::cout << "Error! " << file << " not exists!" << std::endl;
				return FileVersionInfo();
			}

			// 00-GetFileVersionInfoSize
			DWORD dwVerInfoHandle = 0;
			DWORD dwVerInfoSize = GetFileVersionInfoSize(file.c_str(), &dwVerInfoHandle);

			FileVersionInfo ret;
			memset(&ret.fixedFileInfo, 0, sizeof(VS_FIXEDFILEINFO));
			if (dwVerInfoSize <= 0)
				return ret;

			// 01-GetFileVersionInfo
			char *vBuffer = new char[dwVerInfoSize];
			if(vBuffer) memset(vBuffer, 0, dwVerInfoSize);
			if (!vBuffer || !GetFileVersionInfo(file.c_str(), dwVerInfoHandle, dwVerInfoSize, vBuffer))
				return ret;


			// 02-Query the file version info
			// 02-1 Get Fixed File Info
			UINT uLen = 0;
			VS_FIXEDFILEINFO *fixedFileInfo = nullptr;
			if (VerQueryValue(vBuffer, "\\", (void **)&fixedFileInfo, &uLen))
			{
				memcpy(&ret.fixedFileInfo, fixedFileInfo, sizeof(VS_FIXEDFILEINFO));
			}

			// 02-2 Get VarFileInfo
			std::string langcp = GetVarFileInfo(vBuffer);
			std::string strTmp = std::string("\\StringFileInfo\\") + langcp + "\\";

			// 02-3 Get StringFileInfo
			LPVOID lpBuffer = NULL;
			//产品名称
			std::string subblock = strTmp + "ProductName";
			ret.productName = GetStringFileInfo(vBuffer, subblock);
			//产品版本
			subblock = strTmp + "ProductVersion";
			ret.productVersion = GetStringFileInfo(vBuffer, subblock);
			//出品公司
			subblock = strTmp + "CompanyName";
			ret.companyName = GetStringFileInfo(vBuffer, subblock);

			delete[] vBuffer;

			return ret;
		}

		VersionCompareResult CompareProductVersion(const FileVersionInfo &first, const FileVersionInfo &second)
		{
			VersionCompareResult res = VersionCompareResult::VR_Na;

			if (!first.fixedFileInfo.dwSignature && !second.fixedFileInfo.dwSignature)
				res = VersionCompareResult::VR_Both_Unknown;
			else if (!first.fixedFileInfo.dwSignature)
				res = VersionCompareResult::First_Unknown;
			else if (!second.fixedFileInfo.dwSignature)
				res = VersionCompareResult::Second_Unknown;
			else
			{
				long long firstPv = (long long)first.fixedFileInfo.dwProductVersionMS << 32 + first.fixedFileInfo.dwProductVersionLS;
				long long secondPv = (long long)second.fixedFileInfo.dwProductVersionMS << 32 + second.fixedFileInfo.dwProductVersionLS;
				auto firstPhase = (int)GetVersionPhase(first);
				auto secondPhase = (int)GetVersionPhase(second);

				if (firstPv > secondPv)
				{
					res = VersionCompareResult::First_New;
				}
				else if (firstPv < secondPv)
				{
					res = VersionCompareResult::Second_New;
				}
				else if (firstPhase > secondPhase)
				{
					res = VersionCompareResult::First_New;
				}
				else if (firstPhase < secondPhase)
				{
					res = VersionCompareResult::Second_New;
				}
				else
				{
					res = VersionCompareResult::VR_Same;
				}
			}

			return res;
		}

		VersionCompareResult CompareProductVersion(const std::string &first, const std::string &second)
		{
			auto firstInfo = GetFileVersionInfoLW(first);
			auto secondInfo = GetFileVersionInfoLW(first);
			return CompareProductVersion(firstInfo, secondInfo);
		}

		VersionPhase GetVersionPhase(const FileVersionInfo &info)
		{
			VersionPhase ret = VersionPhase::VP_Na;
			auto ver = info.productVersion;
			auto start = ver.find('_');
			if (start == ver.npos)
			{
				return ret;
			}
			auto phaseStr = ver.substr(start + 1);

			//std::transform(phaseStr.begin(), phaseStr.end(), phaseStr.begin(), ::tolower);
			phaseStr = strlwr(phaseStr.data());

			if (phaseStr == "alpha")
			{
				ret = VersionPhase::VP_Alpha;
			}
			else if (phaseStr == "beta")
			{
				ret = VersionPhase::VP_Beta;
			}
			else if (phaseStr == "release")
			{
				ret = VersionPhase::VP_Release;
			}
			else
			{
				ret = VersionPhase::VP_Na;
			}

			return ret;
		}
	}
}